﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Globalization;
using System.Linq;
using Ext.Net;
using Hybrid;

namespace DSS.HybridClasses
{
    public class Variety//множества всех точек, пользовательские
    {
        public int NumVarieties { get; set; }//количество множеств пользователя
        public List<Point> AllPoints = new List<Point>();//список всех точек
        public Grid Grid { get; private set; }//таблица
        public List<Point> ListOfPrefPoints = new List<Point>();//список точек, указанных пользователем
        public List<List<Boolean>> AdjencyMatrix = new List<List<bool>>();//матрица смежности
        public Variety(Grid g)
        { Grid = g; }


        public void MakeVariety(Grid g)//создать множество всех точек
        {
            var stack = new Stack<Gradation>();//стек
            var gradArr = new Gradation[CriterionNum.Num + 1];//для записи из стека
            var elm = new Gradation { NumInGrid = -1, CriteriaNum = 0 };//текущий элемент
            stack.Push(elm);//-1
            elm = SearchNext(elm, g);
            stack.Push(elm);
            Gradation param = elm.Copy();
            ++param.CriteriaNum;
            param.NumInGrid = -1;
            while (stack.Count != 0)
            {
                elm = SearchNext(param, g);
                if (elm.NumInGrid == -1)//не найден- извлекаем
                {
                    if (stack.Count == Grid.ListOfCriterias.Count + 1)//уже много, хватит
                    {
                        stack.CopyTo(gradArr, 0);
                        var p = new Point();
                        for (byte z = 0; z < gradArr.Length - 1; ++z)
                            p.ListOfGradations.Add(gradArr[z]);
                        AllPoints.Add(p);
                    }
                    param = stack.Pop().Copy();
                }
                else
                {
                    if (stack.Count == Grid.ListOfCriterias.Count + 1)//уже много, хватит
                    {
                        stack.CopyTo(gradArr, 0);
                        var p = new Point();
                        for (byte z = 0; z < gradArr.Length - 1; ++z)
                            p.ListOfGradations.Add(gradArr[z]);
                        AllPoints.Add(p);
                        param = stack.Pop().Copy();
                    }
                    else//мало, еще надо
                    {
                        stack.Push(elm);
                        param.NumInGrid = -1;//след строка от первого элемента
                        param.CriteriaNum += 1;
                    }
                }
            }

        }
        //поиск следующего элемента
        public Gradation SearchNext(Gradation param, Grid g)
        {
            if (param.CriteriaNum <= Grid.ListOfCriterias.Count - 1)//можно искать
            {
                if (param.NumInGrid == -1)//поиск от начала
                {
                    return (Grid.ListOfCriterias[param.CriteriaNum].ListOfGradations[0]);
                }
                if (param.NumInGrid != -1)
                {
                    if (param.NumInGrid < Grid.ListOfCriterias[param.CriteriaNum].ListOfGradations.Count - 1)//количество
                        return (Grid.ListOfCriterias[param.CriteriaNum].ListOfGradations[param.NumInGrid + 1]);
                    var ret = new Gradation { NumInGrid = -1 };
                    return (ret);
                }
            }
            return new Gradation();
        }

        /// <summary>
        /// множества пользователя
        /// </summary>
        public void GetVarietyPoints()//считать точки, входящие в множества
        {
            // SqlConnection connection = DBConnection.Open();
            var conString = ConfigurationManager.ConnectionStrings["DSSConnectionString"].ConnectionString;
            var connection = new SqlConnection(conString);
            connection.Open();
            SqlCommand command = connection.CreateCommand();
            command.CommandText = @"SELECT COUNT (*) FROM dbo.preference_set";
            NumVarieties = (int)command.ExecuteScalar();
            var preferencesVariety = new List<int>();
            command.CommandText = String.Format("SELECT ID FROM dbo.Preference_set");
            SqlDataReader sdr = command.ExecuteReader();
            while (sdr.Read())
            {
                preferencesVariety.Add(sdr.GetInt32(0));
            }
            sdr.Close();
            foreach (int i in preferencesVariety) //по всем предпочтениям
            {
                var startIndex = ListOfPrefPoints.Count;

                command.CommandText = String.Format(@"SELECT ps.id as pref_set_id, 
                    cs.criteria_id as criteria_id, cs.ord as subId, ps.color as color
                    FROM dbo.preference_set ps, dbo.pref_set_element pse, dbo.crit_scale cs
                    WHERE ps.id=pse.pref_set_id AND pse.crit_scale_id=cs.id AND ps.id={0}
                    ORDER BY cs.criteria_id DESC", i);


                sdr = command.ExecuteReader();
                var p = new Point { IsPoint = false, IsVariety = true, PreferenceId = i };
                var lastcriterion = 0;
                ListOfPrefPoints.Add(p);
                while (sdr.Read())
                {
                    p.Color = (string)sdr["color"];
                    Gradation gr;
                    if (lastcriterion == Convert.ToInt32(sdr["criteria_id"]))
                    //если градация с таким номером критерия была
                    {
                        int z = ListOfPrefPoints.Count;
                        for (int k = startIndex; k < z; ++k)
                        {
                            gr = new Gradation();
                            var newPoint = ListOfPrefPoints[k].CopyPointExcepLastGradation();
                            //копируем все кроме последней градации
                            gr.CriteriaNum = Convert.ToInt32(sdr["criteria_id"]); //заполняем последнюю градацию
                            newPoint.PreferenceId = ListOfPrefPoints[k].PreferenceId;
                            gr.NumInGrid = Convert.ToInt32(sdr["subId"]);
                            //  gr.I = sdr.GetInt32(2);
                            newPoint.ListOfGradations.Add(gr);
                            newPoint.Color = Convert.ToString(sdr["color"]);
                            ListOfPrefPoints.Add(newPoint);
                        }
                        continue; //читаем следующую строку
                    }
                    gr = new Gradation();
                    lastcriterion = gr.CriteriaNum = Convert.ToInt32(sdr["criteria_id"]); //новый последний
                    // sdr.GetInt32(1);
                    gr.NumInGrid = Convert.ToInt32(sdr["subId"]);
                    //  gr.I = sdr.GetInt32(2);
                    for (int k = startIndex; k < ListOfPrefPoints.Count; ++k)
                        ListOfPrefPoints[k].ListOfGradations.Add(gr);
                }
                sdr.Close();
            }
            connection.Close();

            foreach (Point t in AllPoints)
            {
                foreach (Point t1 in ListOfPrefPoints)
                {
                    if (t != t1) continue;
                    t.IsPoint = false;
                    t.Color = t1.Color;
                    t.IsVariety = true;
                    t.PreferenceId = t1.PreferenceId;
                }
            }
        }


        //создание матрицы смежности
        public void MakeAdjacencyMatrix(int parId)
        {
            int a;
            var dt = HybridClasses.Grid.GetDataTableForComboBox(parId.ToString(), out a);

            var dimension = AllPoints.Count - ListOfPrefPoints.Count + NumVarieties;//размерность
            # region создание матрицы
            for (byte i = 0; i < dimension; ++i)
            {
                var l = new List<Boolean>();
                for (byte j = 0; j < dimension; ++j)
                {
                    l.Add(false);
                }
                AdjencyMatrix.Add(l);
            }
            #endregion
            for (var i = 0; i < AllPoints.Count; ++i)
            {
                for (var j = 0; j < AllPoints.Count; ++j)
                {
                    if (i == j)
                    {
                        AdjencyMatrix[AllPoints[i].IdInAdjencyMatrix][AllPoints[j].IdInAdjencyMatrix] = false;
                        continue;
                    }
                    if ((AllPoints[i].IsPoint) || (AllPoints[j].IsPoint))//хоть 1 точка
                    {
                        if (AllPoints[i].Level == AllPoints[j].Level)//1 уровень
                            AdjencyMatrix[AllPoints[i].IdInAdjencyMatrix][AllPoints[j].IdInAdjencyMatrix] =
                                Point.DominatingOnPodvinsky(AllPoints[i], AllPoints[j], Grid);

                        else//разные уровни
                            AdjencyMatrix[AllPoints[i].IdInAdjencyMatrix][AllPoints[j].IdInAdjencyMatrix] =
                                Point.DominatingOnPareto(AllPoints[i], AllPoints[j]);
                    }
                    else//2 множества
                    {
                        var r1 = 0.0;
                        foreach (DataRow row in dt.Rows.Cast<DataRow>().Where(row => row["ScaleName"] == AllPoints[i].Color))
                        {
                            r1 = Convert.ToDouble(row["ScaleValue"]);
                        }

                        var r2 = 0.0;
                        foreach (DataRow row in dt.Rows.Cast<DataRow>().Where(row => row["ScaleName"] == AllPoints[j].Color))
                        {
                            r1 = Convert.ToDouble(row["ScaleValue"]);
                        }
                        if (r1 > r2)

                            AdjencyMatrix[AllPoints[i].IdInAdjencyMatrix][AllPoints[j].IdInAdjencyMatrix] = true;
                        else
                            AdjencyMatrix[AllPoints[i].IdInAdjencyMatrix][AllPoints[j].IdInAdjencyMatrix] = false;
                    }
                }
            }
        }



        public void SortPointsForPareto()//по уровням, которые нельзя сравнивать по парето
        //в двумерном пространнстве что-то типа y=-x
        {
            foreach (var p in AllPoints)//установить уровни, чтоб было что сравнивать
                p.SetLevel();
            for (var i = 0; i < AllPoints.Count - 1; ++i)
                for (int j = 0; j < AllPoints.Count - 1 - i; ++j)
                    if (AllPoints[j].Level > AllPoints[j + 1].Level)
                    {
                        var temp = AllPoints[j].FullCopy();
                        AllPoints[j] = AllPoints[j + 1];
                        AllPoints[j + 1] = temp;
                    }

            int r = 0;
            //установить номера в таблице
            for (var i = 0; i < AllPoints.Count; ++i)
            {
                if (AllPoints[i].IsPoint)
                {
                    AllPoints[i].IdInAdjencyMatrix = r;
                    ++r;
                }
                else
                {
                    bool founded = false;
                    for (int j = 0; j < i; ++j)
                    {
                        if ((AllPoints[i].PreferenceId == AllPoints[j].PreferenceId) &&
                            (AllPoints[j].IsVariety) && (AllPoints[j].IsVariety))
                        {
                            AllPoints[i].IdInAdjencyMatrix = AllPoints[j].IdInAdjencyMatrix;
                            founded = true;
                            break;
                        }
                    }
                    if (!founded)
                    {
                        AllPoints[i].IdInAdjencyMatrix = r;
                        ++r;
                    }
                }
            }
        }


        public Boolean DetectCycles()//проверка на цикличность, нерекурсивный поиск в глубину
        //чтоб не вываливался из-за переполнения стека
        {
            var cycl = new List<int>();
            bool cycleFound = false;//нет цикла
            int maxn = AdjencyMatrix.Count;
            const int white = 0, grey = 1, black = 2;
            for (int z = 0; z < maxn; ++z)
            {
                var s = new Stack<int>();
                var color = new int[maxn];
                for (int i = 0; i < maxn; ++i)
                    color[i] = white;
                s.Push(z);//начальная точка
                while (s.Count != 0)
                {
                    int from = s.Peek();
                    if (color[from] == white)
                    {
                        color[from] = grey;
                        for (int to = 0; to < maxn; ++to)
                        {
                            if (AdjencyMatrix[from][to])
                                if (color[to] == white)
                                    s.Push(to);
                                else
                                    if (color[to] == grey)
                                    {
                                        cycleFound = true; // найден цикл
                                        if (!cycl.Contains(to))
                                            cycl.Add(to);
                                    }
                        }
                    }
                    else
                    { // вершина помеченна, значит мы в нее пришли уже на выходе из рекурсии из потомков
                        color[from] = black;
                        s.Pop();
                    }
                }
            }
            var cycled = ListOfPrefPoints.Aggregate("", (current1, t) => cycl.Where(j => t.IdInAdjencyMatrix == j).Aggregate
                (current1, (current, j) => current + ("PreferenceID: " + t.PreferenceId.ToString(CultureInfo.InvariantCulture) + ";<br/>")));
            if (cycleFound)
                X.Msg.Alert("Cycled", cycled).Show();
            else
                X.Msg.Notify("Acycled", "").Show();
            return cycleFound;
        }


        public void GraphAnalysis()
        {
            int q = AllPoints.Count;
            int m = ListOfPrefPoints.Count;
            int p = q - m + q;//для определения уровня
            var excluded = new List<int>();//уже исключены

            bool excludedpoint = false;//исключается ли вершина
            int maxn = AdjencyMatrix.Count;//размерность
            var excluding = new List<int>();//исключаемые на данной итерации(недоминируемые)
            while (excluded.Count != maxn)
            {
                excluding.Clear();
                for (int i = 0; i < maxn; ++i)//по столбцам
                {
                    if (excluded.Contains(i)) continue;//вычеркнуто
                    for (int j = 0; j < maxn; ++j)
                    {
                        if (excluded.Contains(j))
                            continue;
                        if (AdjencyMatrix[j][i])//сюда можно попась
                        {
                            excludedpoint = false;
                            break;
                        }
                        excludedpoint = true;
                    }
                    if (excludedpoint)
                        excluding.Add(i);//вычеркиваем
                }
                foreach (var i in excluding)//устанавливаем уровни недоминируемым
                    foreach (Point t in AllPoints)
                        if (t.IdInAdjencyMatrix == i)
                            t.FinalLevel = p;
                --p;
                excluded.AddRange(excluding);
            }
            Double z = p * 100 / (q - m + q - 1);//показатель различимости
            X.Msg.Notify("result", z.ToString()).Show();
        }



        public void SaveResult(int parID)
        {
            var conString = ConfigurationManager.ConnectionStrings["DSSConnectionString"].ConnectionString;
            var connection = new SqlConnection(conString);
            var command = connection.CreateCommand();
            command.CommandText = @"";
            int gri = 0;
            for (int i = 0; i < AllPoints.Count; ++i)
            {
                var point = AllPoints[i];
                command.CommandText = @"";
                foreach (var gr in point.ListOfGradations)
                {
                    command.CommandText = String.Format(@"declare @crit_scale_id int
set @crit_scale_id = (select 1 from issdss.dbo.crit_scale where  id={0} and ord={1})",
                        gr.CriteriaNum, gr.NumInGrid);

                    command.CommandText += @"INSERT INTO dbo.[pref_set_element] (id, pref_set_id, crit_scale_id)
                    VALUES (@id, @pref_set_id, @crit_scale_id)";
                    command.Parameters.AddWithValue("@id", gri);
                    command.Parameters.AddWithValue(@"pref_set_id", i);
                    command.ExecuteNonQuery();
                    gri++;

                }
                if (point.IsPoint)
                {
                    command.CommandText = @"INSERT INTO dbo.[preference_set] (id, crit_scale_id, rank)
                    VALUES (@id, @crit_scale_id, @rank)";
                    command.Parameters.AddWithValue("@id", i);

                    command.Parameters.AddWithValue(@"crit_scale_id", null);
                    command.Parameters.AddWithValue("rank", point.FinalLevel);
                    command.ExecuteNonQuery();
                }
                else
                {
                    command.CommandText = @"INSERT INTO dbo.[preference_set] (id, crit_scale_id, rank)
                    VALUES (@id, @crit_scale_id, @rank)";
                    command.Parameters.AddWithValue("@id", i);
                      command.Parameters.AddWithValue("@crit_scale_id", parID);
                    command.Parameters.AddWithValue("rank", point.FinalLevel);
                    command.ExecuteNonQuery();
                }
            }
        }
    }
}
